/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.replica.replicaisland;

import com.replica.replicaisland.SoundSystem.Sound;

/**
 * This component allows objects to die and be deleted when their life is
 * reduced to zero or they meet other configurable criteria.
 */
public class LifetimeComponent extends GameComponent {
	private boolean mDieWhenInvisible;
	private float mTimeUntilDeath;
	private GameObjectFactory.GameObjectType mSpawnOnDeathType;
	private LaunchProjectileComponent mTrackingSpawner;
	private Vector2 mHotSpotTestPoint;
	private boolean mReleaseGhostOnDeath;
	private boolean mVulnerableToDeathTiles;
	private boolean mDieOnHitBackground;
	private Sound mDeathSound;
	private boolean mIncrementEventCounter;
	private int mEventCounter;

	public LifetimeComponent() {
		super();
		mHotSpotTestPoint = new Vector2();
		reset();
		setPhase(ComponentPhases.THINK.ordinal());
	}

	@Override
	public void reset() {
		mDieWhenInvisible = false;
		mTimeUntilDeath = -1;
		mSpawnOnDeathType = GameObjectFactory.GameObjectType.INVALID;
		mTrackingSpawner = null;
		mHotSpotTestPoint.zero();
		mReleaseGhostOnDeath = true;
		mVulnerableToDeathTiles = false;
		mDieOnHitBackground = false;
		mDeathSound = null;
		mIncrementEventCounter = false;
		mEventCounter = -1;
	}

	public void setDieWhenInvisible(boolean die) {
		mDieWhenInvisible = die;
	}

	public void setTimeUntilDeath(float time) {
		mTimeUntilDeath = time;
	}

	public void setObjectToSpawnOnDeath(GameObjectFactory.GameObjectType type) {
		mSpawnOnDeathType = type;
	}

	public void setIncrementEventCounter(int event) {
		mIncrementEventCounter = true;
		mEventCounter = event;
	}

	@Override
	public void update(float timeDelta, BaseObject parent) {
		GameObject parentObject = (GameObject) parent;
		if (mTimeUntilDeath > 0) {
			mTimeUntilDeath -= timeDelta;
			if (mTimeUntilDeath <= 0) {
				die(parentObject);
				return;
			}
		}

		if (mDieWhenInvisible) {
			CameraSystem camera = sSystemRegistry.cameraSystem;
			ContextParameters context = sSystemRegistry.contextParameters;
			final float dx = Math.abs(parentObject.getPosition().x
					- camera.getFocusPositionX());
			final float dy = Math.abs(parentObject.getPosition().y
					- camera.getFocusPositionY());
			if (dx > context.gameWidth || dy > context.gameHeight) {
				// the position of this object is off the screen, destroy!
				// TODO: this is a pretty dumb test. We should have a bounding
				// volume instead.
				die(parentObject);
				return;
			}
		}

		if (parentObject.life > 0 && mVulnerableToDeathTiles) {
			HotSpotSystem hotSpot = sSystemRegistry.hotSpotSystem;
			if (hotSpot != null) {
				// TODO: HACK! Unify all this code.
				if (hotSpot.getHotSpot(parentObject.getCenteredPositionX(),
						parentObject.getPosition().y + 10.0f) == HotSpotSystem.HotSpotType.DIE) {
					parentObject.life = 0;
				}
			}
		}

		if (parentObject.life > 0 && mDieOnHitBackground) {
			if (parentObject.getBackgroundCollisionNormal().length2() > 0.0f) {
				parentObject.life = 0;
			}
		}

		if (parentObject.life <= 0) {
			die(parentObject);
			return;
		}
	}

	private void die(GameObject parentObject) {
		GameObjectFactory factory = sSystemRegistry.gameObjectFactory;
		GameObjectManager manager = sSystemRegistry.gameObjectManager;

		if (mReleaseGhostOnDeath) {
			// TODO: This is sort of a hack. Find a better way to do this
			// without introducing a
			// dependency between these two. Generic on-death event or
			// something.
			GhostComponent ghost = parentObject
					.findByClass(GhostComponent.class);
			if (ghost != null) {
				ghost.releaseControl(parentObject);
			}
		}

		if (mIncrementEventCounter) {
			EventRecorder recorder = sSystemRegistry.eventRecorder;
			recorder.incrementEventCounter(mEventCounter);
		}

		if (mSpawnOnDeathType != GameObjectFactory.GameObjectType.INVALID) {
			GameObject object = factory.spawn(mSpawnOnDeathType,
					parentObject.getPosition().x, parentObject.getPosition().y,
					parentObject.facingDirection.x < 0.0f);

			if (object != null && manager != null) {
				manager.add(object);
			}

		}

		if (mTrackingSpawner != null) {
			mTrackingSpawner.trackedProjectileDestroyed();
		}

		if (manager != null) {
			manager.destroy(parentObject);
		}

		if (mDeathSound != null) {
			SoundSystem sound = sSystemRegistry.soundSystem;
			if (sound != null) {
				sound.play(mDeathSound, false, SoundSystem.PRIORITY_NORMAL);
			}
		}

	}

	public final void setTrackingSpawner(LaunchProjectileComponent spawner) {
		mTrackingSpawner = spawner;
	}

	public final void setReleaseGhostOnDeath(boolean release) {
		mReleaseGhostOnDeath = release;
	}

	public final void setVulnerableToDeathTiles(boolean vulnerable) {
		mVulnerableToDeathTiles = vulnerable;
	}

	public final void setDieOnHitBackground(boolean die) {
		mDieOnHitBackground = die;
	}

	public final void setDeathSound(Sound deathSound) {
		mDeathSound = deathSound;
	}
}
